package com.zq.it.cms.common;

import java.io.IOException;
import java.io.UnsupportedEncodingException;
import java.math.BigDecimal;
import java.net.URLDecoder;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Date;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import java.util.regex.Pattern;

import javax.servlet.ServletRequest;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;

import org.apache.commons.lang.ArrayUtils;
import org.apache.commons.lang.StringUtils;
import org.apache.commons.lang.Validate;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Sort;
import org.springframework.data.domain.Sort.Direction;
import org.springframework.data.domain.Sort.Order;
import org.springframework.http.HttpHeaders;
import org.springframework.util.Assert;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import org.springframework.web.util.UriComponents;
import org.springframework.web.util.UriComponentsBuilder;

import com.zq.it.cms.common.util.CommUtil;

/**
 * Servlet工具类
 * 
 */
public class Servlets {
	private static final Logger logger = LoggerFactory.getLogger(Servlets.class);

	private static final String NUKNOWN = "unknown";
	private static final String[] ADDR_HEADER = { "X-Forwarded-For", "Proxy-Client-IP", "WL-Proxy-Client-IP", "X-Real-IP" };

	/**
	 * 获得真实IP地址。在使用了反向代理时，直接用HttpServletRequest.getRemoteAddr()无法获取客户真实的IP地址。
	 * 
	 * @param request
	 * @return
	 */
	public static String getRemoteAddr(ServletRequest request) {
		String addr = null;
		if (request instanceof HttpServletRequest) {
			HttpServletRequest hsr = (HttpServletRequest) request;
			for (String header : ADDR_HEADER) {
				if (StringUtils.isBlank(addr) || NUKNOWN.equalsIgnoreCase(addr)) {
					addr = hsr.getHeader(header);
				} else {
					break;
				}
			}
		}
		if (StringUtils.isBlank(addr) || NUKNOWN.equalsIgnoreCase(addr)) {
			addr = request.getRemoteAddr();
		} else {
			// 对于通过多个代理的情况，第一个IP为客户端真实IP,多个IP按','分割
			int i = addr.indexOf(",");
			if (i > 0) {
				addr = addr.substring(0, i);
			}
		}
		return addr;
	}

	/**
	 * 设置让浏览器弹出下载对话框的Header.
	 * 
	 * @param filename
	 *            下载后的文件名.
	 */
	public static void setDownloadHeader(HttpServletResponse response, String filename) {
		response.setHeader(HttpHeaders.CONTENT_DISPOSITION, "attachment; filename=\"" + filename + "\"");
	}

	/**
	 * 获取请求上的分页和排序参数
	 */
	public static PageRequest getPageRequest(){
		HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
		PageRequest pageRequest = null;
		if(request == null){
			pageRequest = new PageRequest(0, Constants.PAGE_SIZE);
		}else{
			Integer page = CommUtil.toInt(request.getParameter(Constants.PAGE_PREFIX + "number"));
			if(page == null || page < 1){
				page = 1;
			}
			page--;
			Integer rows = CommUtil.toInt(request.getParameter(Constants.PAGE_PREFIX + "size"));
			if(rows == 0){
				rows = Constants.PAGE_SIZE;
			}
			List<Order> orderList = new ArrayList<Order>();
			String sorts[] = request.getParameterValues(Constants.PAGE_PREFIX + "sort");
			if(sorts != null && sorts.length > 0){
				for (String sort : sorts) {
					if(StringUtils.isNotBlank(sort)){
						Direction dir = Direction.ASC;
						String sord[] = sort.split(",");
						if(sord.length == 2){
							if(StringUtils.isNotBlank(sord[1]) && sord[1].equalsIgnoreCase("desc")){
								dir = Direction.DESC;
							}
						}
						orderList.add(new Order(dir, sord[0]));
					}
				}
			}
			pageRequest = new PageRequest(page, rows, orderList.isEmpty() ? null : new Sort(orderList));
		}
		return pageRequest;
	}
	
	/**
	 * 获取jqGrid的分页和排序参数
	 */
	public static PageRequest getJqGridPageRequest(){
		HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
		PageRequest pageRequest = null;
		if(request==null){
			pageRequest = new PageRequest(0, Constants.PAGE_SIZE);
		}else{
			Integer page = CommUtil.toInt(request.getParameter("page"));
			if(page == null || page < 1){
				page = 1;
			}
			page--;
			Integer rows = CommUtil.toInt(request.getParameter("rows"));
			if(rows == 0){
				rows = Constants.PAGE_SIZE;
			}
			String sidx = request.getParameter("sidx");
			String sord = request.getParameter("sord");
			if(StringUtils.isNotBlank(sidx)){
				Direction dir = Direction.ASC;
				if(StringUtils.isBlank(sord) || Direction.DESC.toString().equals(sord.toUpperCase())){
					dir =  Direction.DESC;
				}
				pageRequest = new PageRequest(page, rows, dir, sidx);
			}else{
				pageRequest = new PageRequest(page, rows);
			}
		}
		return pageRequest;
	}
	
	/**
	 * 获取搜索条件
	 */
	public static Map<String, Object> getSearchParam(){
		HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
		Map<String, String> paramMap = Servlets.getParamMap(request, Constants.SEARCH_PREFIX);
		Map<String, Object> params = new HashMap<String, Object>();
		for(String key : paramMap.keySet()){
			if(Pattern.matches("^[\\w\\.]+:[\\w\\.]+:[\\w\\.]+$", key)){
				String className = key.substring(key.lastIndexOf(":") + 1);
				try {
					Class<?> claz = Class.forName(className);
					if(claz == int.class || claz == Integer.class){
						params.put(key, CommUtil.toInt(paramMap.get(key)));
					}else if(claz == long.class || claz == Long.class){
						params.put(key, CommUtil.toLong(paramMap.get(key)));
					}else if(claz == float.class || claz == Float.class){
						params.put(key, CommUtil.toFloat(paramMap.get(key)));
					}else if(claz == double.class || claz == Double.class){
						params.put(key, CommUtil.toDouble(paramMap.get(key)));
					}else if(claz == BigDecimal.class){
						params.put(key, CommUtil.toBigDecimal(paramMap.get(key)));
					}else if(claz == Date.class){
						params.put(key, CommUtil.parseDate(paramMap.get(key)));
					}else{
						params.put(key, paramMap.get(key));
					}
				} catch (Exception e) {
					throw new RuntimeException(e.getMessage());
				}
			}else{
				params.put(key, paramMap.get(key));
			}
		}
		return params;
	}
	
	public static Map<String, String[]> parseQueryString(String queryString) {
		if (StringUtils.isBlank(queryString)) {
			return Collections.emptyMap();
		}
		Map<String, String[]> queryMap = new TreeMap<String, String[]>();
		String[] params = StringUtils.split(queryString, '&');
		for (String param : params) {
			int index = param.indexOf('=');
			if (index != -1) {
				String name = param.substring(0, index);
				// name为空值不保存
				if (StringUtils.isBlank(name)) {
					continue;
				}
				String value = param.substring(index + 1);
				try {
					value = URLDecoder.decode(value, "UTF-8");
				} catch (UnsupportedEncodingException e) {
					logger.error("never!", e);
				}
				if (queryMap.containsKey(name)) {
					String[] values = queryMap.get(name);
					String[] values2 = new String[values.length + 1];
					for(int i = 0;i < values.length;i++){
						values2[i] = values[i];
					}
					values2[values.length] = value;
					queryMap.put(name, values2);
				} else {
					queryMap.put(name, new String[] { value });
				}
			}
		}
		return queryMap;
	}

	public static String getParam(HttpServletRequest request, Map<String, String[]> queryMap, String name) {
		String[] values = getParamValues(request, queryMap, name);
		return ArrayUtils.isNotEmpty(values) ? StringUtils.join(values, ',') : null;
	}

	public static String getParam(HttpServletRequest request, String name) {
		String[] values = getParamValues(request, name);
		return ArrayUtils.isNotEmpty(values) ? StringUtils.join(values, ',') : null;
	}

	public static String[] getParamValues(HttpServletRequest request, Map<String, String[]> queryMap, String name) {
		Validate.notNull(request, "Request must not be null");
		String[] values = queryMap.get(name);
		if (values == null) {
			values = request.getParameterValues(name);
		}
		return values;
	}

	public static String[] getParamValues(HttpServletRequest request, String name) {
		Validate.notNull(request, "Request must not be null");
		String qs = request.getQueryString();
		Map<String, String[]> queryMap = parseQueryString(qs);
		return getParamValues(request, queryMap, name);
	}

	public static String[] getParamPrefix(HttpServletRequest request, String prefix) {
		Validate.notNull(request, "Request must not be null");
		Map<String, String[]> params = getParamValuesMap(request, prefix);
		List<String> values = new ArrayList<String>();
		for (Entry<String, String[]> entry : params.entrySet()) {
			values.addAll(Arrays.asList(entry.getValue()));
		}
		return values.toArray(new String[values.size()]);
	}

	public static Map<String, String> getParamMap(HttpServletRequest request, String prefix) {
		return getParamMap(request, prefix, false);
	}

	@SuppressWarnings("rawtypes")
	public static Map<String, String> getParamMap(HttpServletRequest request, String prefix, boolean keyWithPrefix) {
		Validate.notNull(request, "Request must not be null");
		Map<String, String> params = new LinkedHashMap<String, String>();
		if (prefix == null) {
			prefix = "";
		}
		String qs = request.getQueryString();
		Map<String, String[]> queryMap = parseQueryString(qs);
		int len = prefix.length();
		Enumeration paramNames = request.getParameterNames();
		while (paramNames != null && paramNames.hasMoreElements()) {
			String paramName = (String) paramNames.nextElement();
			if ("".equals(prefix) || paramName.startsWith(prefix)) {
				String name = keyWithPrefix ? paramName : paramName.substring(len);
				String value = getParam(request, queryMap, paramName);
				if (StringUtils.isNotBlank(value)) {
					params.put(name, value);
				}
			}
		}
		return params;
	}

	public static Map<String, String[]> getParamValuesMap(HttpServletRequest request, String prefix) {
		return getParamValuesMap(request, prefix, false);
	}

	@SuppressWarnings("rawtypes")
	public static Map<String, String[]> getParamValuesMap(HttpServletRequest request, String prefix, boolean keyWithPrefix) {
		Validate.notNull(request, "Request must not be null");
		Enumeration paramNames = request.getParameterNames();
		Map<String, String[]> params = new LinkedHashMap<String, String[]>();
		if (prefix == null) {
			prefix = "";
		}
		String qs = request.getQueryString();
		Map<String, String[]> queryMap = parseQueryString(qs);
		int len = prefix.length();
		while (paramNames != null && paramNames.hasMoreElements()) {
			String paramName = (String) paramNames.nextElement();
			if ("".equals(prefix) || paramName.startsWith(prefix)) {
				String name = keyWithPrefix ? paramName : paramName.substring(len);
				String[] values = getParamValues(request, queryMap, paramName);
				if (values != null && values.length > 0) {
					params.put(name, values);
				}
			}
		}
		return params;
	}

	/**
	 * 设置禁止客户端缓存的Header.
	 */
	public static void setNoCacheHeader(HttpServletResponse response) {
		// Http 1.0 header
		response.setDateHeader("Expires", 1L);
		response.addHeader("Pragma", "no-cache");
		// Http 1.1 header
		response.setHeader("Cache-Control", "no-cache, no-store, max-age=0");
	}

	/**
	 * 输出html。并禁止客户端缓存。输出json也可以用这个方法。
	 * 
	 * contentType:text/html;charset=utf-8。
	 * 
	 * @param response
	 * @param s
	 */
	public static void writeHtml(HttpServletResponse response, String s) {
		response.setContentType("text/html;charset=utf-8");
		setNoCacheHeader(response);
		try {
			response.getWriter().write(s);
		} catch (IOException ex) {
			logger.error(ex.getMessage(), ex);
		}
	}

	public static String getCookie(HttpServletRequest request, String name) {
		Assert.notNull(request, "Request must not be null");
		Cookie cookies[] = request.getCookies();
		if (cookies != null) {
			for (Cookie cookie : cookies) {
				if (name.equals(cookie.getName())) {
					return cookie.getValue();
				}
			}
		}
		return null;
	}

	public static boolean validateUrl(String url, Set<String> validDomains) {
		if (StringUtils.isBlank(url)) {
			return true;
		}
		UriComponentsBuilder ucb = UriComponentsBuilder.fromUriString(url);
		UriComponents uc = ucb.build();
		String host = uc.getHost();
		if (StringUtils.isBlank(host) || validDomains.contains(host)) {
			return true;
		} else {
			return false;
		}
	}
}
